package org.exhaust.framework.net.common.protocol;

import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.*;
import io.netty.channel.kqueue.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;

import java.util.concurrent.ThreadFactory;

public class EventLoopFactory {

    public static final String OS_NAME_KEY = "os.name";

    public static final String OS_LINUX_PREFIX = "linux";

    public static boolean isEpollAvailable() {
        String osName = System.getProperty(OS_NAME_KEY);
        return osName.toLowerCase().contains(OS_LINUX_PREFIX) && Epoll.isAvailable();
    }

    public static boolean isKqueueAvailable() {
        return KQueue.isAvailable();
    }

    public static EventLoopGroup eventLoopGroup(int threads, String threadFactoryName) {
        ThreadFactory threadFactory = new DefaultThreadFactory(threadFactoryName, true);
        if (isEpollAvailable()) {
            return new EpollEventLoopGroup(threads, threadFactory);
        }
        if (isKqueueAvailable()) {
            return new KQueueEventLoopGroup(threads, threadFactory);
        }
        return new NioEventLoopGroup(threads, threadFactory);
    }

    public static Class<? extends DatagramChannel> datagramChannelClass() {
        if (isEpollAvailable()) {
            return EpollDatagramChannel.class;
        }
        if (isKqueueAvailable()) {
            return KQueueDatagramChannel.class;
        }
        return NioDatagramChannel.class;
    }

    public static Class<? extends SocketChannel> socketChannelClass() {
        if (isEpollAvailable()) {
            return EpollSocketChannel.class;
        }
        if (isKqueueAvailable()) {
            return KQueueSocketChannel.class;
        }
        return NioSocketChannel.class;
    }

    public static Class<? extends ServerSocketChannel> serverSocketChannelClass() {
        if (isEpollAvailable()) {
            return EpollServerSocketChannel.class;
        }
        if (isKqueueAvailable()) {
            return KQueueServerSocketChannel.class;
        }
        return NioServerSocketChannel.class;
    }
}
